/*
 * Copyright 2016, Gregg Tavares.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Gregg Tavares. nor the names of his
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


(function(root, factory) {  // eslint-disable-line
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], function() {
            return factory.call(root);
        });
    } else {
        // Browser globals
        root.webglLessonsUI = factory.call(root);
    }
}(this, function() {
    'use strict';
    const gopt = getQueryParams();

    function setupSlider(selector, options) {
        var parent = document.querySelector(selector);
        if (!parent) {
            // like jquery don't fail on a bad selector
            return;
        }
        if (!options.name) {
            options.name = selector.substring(1);
        }
        return createSlider(parent, options); // eslint-disable-line
    }

    function createSlider(parent, options) {
        var precision = options.precision || 0;
        var min = options.min || 0;
        var step = options.step || 1;
        var value = options.value || 0;
        var max = options.max || 1;
        var fn = options.slide;
        var name = gopt["ui-" + options.name] || options.name;
        var uiPrecision = options.uiPrecision === undefined ? precision : options.uiPrecision;
        var uiMult = options.uiMult || 1;

        min /= step;
        max /= step;
        value /= step;

        parent.innerHTML = `
      <div class="gman-widget-outer">
        <div class="gman-widget-label">${name}</div>
        <div class="gman-widget-value"></div>
        <input class="gman-widget-slider" type="range" min="${min}" max="${max}" value="${value}" />
      </div>
    `;
        var valueElem = parent.querySelector(".gman-widget-value");
        var sliderElem = parent.querySelector(".gman-widget-slider");

        function updateValue(value) {
            valueElem.textContent = (value * step * uiMult).toFixed(uiPrecision);
        }

        updateValue(value);

        function handleChange(event) {
            var value = parseInt(event.target.value);
            updateValue(value);
            fn(event, { value: value * step });
        }

        sliderElem.addEventListener('input', handleChange);
        sliderElem.addEventListener('change', handleChange);

        return {
            elem: parent,
            updateValue: (v) => {
                v /= step;
                sliderElem.value = v;
                updateValue(v);
            },
        };
    }

    function makeSlider(options) {
        const div = document.createElement("div");
        return createSlider(div, options);
    }

    var widgetId = 0;
    function getWidgetId() {
        return "__widget_" + widgetId++;
    }

    function makeCheckbox(options) {
        const div = document.createElement("div");
        div.className = "gman-widget-outer";
        const label = document.createElement("label");
        const id = getWidgetId();
        label.setAttribute('for', id);
        label.textContent = gopt["ui-" + options.name] || options.name;
        label.className = "gman-checkbox-label";
        const input = document.createElement("input");
        input.type = "checkbox";
        input.checked = options.value;
        input.id = id;
        input.className = "gman-widget-checkbox";
        div.appendChild(label);
        div.appendChild(input);
        input.addEventListener('change', function(e) {
            options.change(e, {
                value: e.target.checked,
            });
        });

        return {
            elem: div,
            updateValue: function(v) {
                input.checked = !!v;
            },
        };
    }

    function makeOption(options) {
        const div = document.createElement("div");
        div.className = "gman-widget-outer";
        const label = document.createElement("label");
        const id = getWidgetId();
        label.setAttribute('for', id);
        label.textContent = gopt["ui-" + options.name] || options.name;
        label.className = "gman-widget-label";
        const selectElem = document.createElement("select");
        options.options.forEach((name, ndx) => {
            const opt = document.createElement("option");
            opt.textContent = gopt["ui-" + name] || name;
            opt.value = ndx;
            opt.selected = ndx === options.value;
            selectElem.appendChild(opt);
        });
        selectElem.className = "gman-widget-select";
        div.appendChild(label);
        div.appendChild(selectElem);
        selectElem.addEventListener('change', function(e) {
            options.change(e, {
                value: selectElem.selectedIndex,
            });
        });

        return {
            elem: div,
            updateValue: function(v) {
                selectElem.selectedIndex = v;
            },
        };
    }

    function noop() {
    }

    function genSlider(object, ui) {
        const changeFn = ui.change || noop;
        ui.name = ui.name || ui.key;
        ui.value = object[ui.key];
        ui.slide = ui.slide || function(event, uiInfo) {
            object[ui.key] = uiInfo.value;
            changeFn();
        };
        return makeSlider(ui);
    }

    function genCheckbox(object, ui) {
        const changeFn = ui.change || noop;
        ui.value = object[ui.key];
        ui.name = ui.name || ui.key;
        ui.change = function(event, uiInfo) {
            object[ui.key] = uiInfo.value;
            changeFn();
        };
        return makeCheckbox(ui);
    }

    function genOption(object, ui) {
        const changeFn = ui.change || noop;
        ui.value = object[ui.key];
        ui.name = ui.name || ui.key;
        ui.change = function(event, uiInfo) {
            object[ui.key] = uiInfo.value;
            changeFn();
        };
        return makeOption(ui);
    }

    const uiFuncs = {
        slider: genSlider,
        checkbox: genCheckbox,
        option: genOption,
    };

    function setupUI(parent, object, uiInfos) {
        const widgets = {};
        uiInfos.forEach(function(ui) {
            const widget = uiFuncs[ui.type](object, ui);
            parent.appendChild(widget.elem);
            widgets[ui.key] = widget;
        });
        return widgets;
    }

    function updateUI(widgets, data) {
        Object.keys(widgets).forEach(key => {
            const widget = widgets[key];
            widget.updateValue(data[key]);
        });
    }

    function getQueryParams() {
        var params = {};
        if (window.hackedParams) {
            Object.keys(window.hackedParams).forEach(function(key) {
                params[key] = window.hackedParams[key];
            });
        }
        if (window.location.search) {
            window.location.search.substring(1).split("&").forEach(function(pair) {
                var keyValue = pair.split("=").map(function(kv) {
                    return decodeURIComponent(kv);
                });
                params[keyValue[0]] = keyValue[1];
            });
        }
        return params;
    }

    return {
        setupUI: setupUI,
        updateUI: updateUI,
        setupSlider: setupSlider,
        makeSlider: makeSlider,
        makeCheckbox: makeCheckbox,
    };

}));
